package com.codezj;

import com.codezj.domain.MethodCall;
import com.codezj.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @Author: baozhongjie
 * @Version: v1.0.0
 * @Description: 方法调用追踪
 **/
public class MethodTracer {

    // 创建 logger，logger 名称会自动使用类的全限定名
    private static final Logger logger = LoggerFactory.getLogger(MethodTracer.class);

    // 使用ThreadLocal存储每个线程的调用栈
    private static final ThreadLocal<Deque<MethodCall>> callStack = new ThreadLocal<Deque<MethodCall>>() {
        @Override
        protected Deque<MethodCall> initialValue() {
            return new ArrayDeque<>();
        }
    };

    // 定期清理ThreadLocal
    private static final ThreadLocal<Integer> callCount = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    // 栈深度限制
    private static final int MAX_STACK_DEPTH = 1000;

    /**
     * 方法入口时调用
     *
     * @param className  类名
     * @param methodName 方法名
     * @param params     入参
     * @param paramTypes 入参类型
     * @param lineNumber 行号
     */
    public static void logMethodEntry(String className, String methodName, Object[] params, Class<?>[] paramTypes, int lineNumber) {
        // 获取当前线程的调用栈
        Deque<MethodCall> stack = callStack.get();

        // 栈深度
        if (stack.size() >= MAX_STACK_DEPTH) {
            logger.warn("[WARN] Call stack depth exceeded limit, clearing stack to prevent memory leak");
            stack.clear();
        }

        // 调用者
        MethodCall methodCaller = stack.isEmpty() ? null : stack.peek();

        // 被调用者
        MethodCall methodCallee = MethodCall.builder()
                .className(className)
                .methodName(methodName)
                .params(formatParam(params))
                .paramTypes(formatParamTypesFromClasses(paramTypes))
                .lineNumber(lineNumber)
                .timestamp(System.currentTimeMillis())
                .build();

        // 输出调用关系链
        // 格式化打印看看
        StringBuilder sb = new StringBuilder();
        if (Objects.isNull(methodCaller)) {
            // caller不存在，认为是入口
            sb.append("[ENTRY] ");
        } else {
            sb.append("[CALL] ");
        }
        sb.append(JsonUtil.toJson(methodCaller)).append(" -> ").append(JsonUtil.toJson(methodCallee));
        logger.info(sb.toString());

        // 将当前方法信息推入调用栈
        stack.push(methodCallee);

        // 增加调用计数，定期清理
        Integer count = callCount.get();
        callCount.set(count + 1);
        if (count > 0 && count % 1000 == 0) {
            cleanupIfNeeded();
        }
    }

    /**
     * 格式化参数
     */
    private static String formatParam(Object[] params) {
        if (params == null || params.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < params.length; i++) {
            if (i > 0) sb.append(",");
            sb.append(params[i]);
        }
        return sb.toString();
    }

    /**
     * 方法退出时调用，从调用栈中弹出当前方法
     */
    public static void logMethodExit(String className, String methodName, Object result) {
        try {
            Deque<MethodCall> stack = callStack.get();

            // 弹出当前方法
            if (!stack.isEmpty()) {
                MethodCall currentMethod = stack.peek();
                if (currentMethod != null &&
                    currentMethod.getClassName().equals(className) &&
                    currentMethod.getMethodName().equals(methodName)) {
                    stack.pop();
                }
            }
        } catch (Exception e) {
            logger.warn("[WARN] Failed to log method exit: {}", e.getMessage());
        }
    }

    /**
     * 从Class数组格式化参数类型信息
     */
    private static String formatParamTypesFromClasses(Class<?>[] paramTypes) {
        if (paramTypes == null || paramTypes.length == 0) {
            return "";
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < paramTypes.length; i++) {
            if (i > 0) sb.append(",");

            String typeName = paramTypes[i].getName();

            // 处理数组类型，将 [Ljava.lang.String; 转换为 java.lang.String[]
            if (typeName.startsWith("[")) {
                sb.append(convertArrayTypeName(typeName));
            } else {
                sb.append(typeName);
            }
        }
        return sb.toString();
    }

    /**
     * 转换数组类型名称
     * [Ljava.lang.String; -> java.lang.String[]
     * [I -> int[]
     * [[Ljava.lang.String; -> java.lang.String[][]
     */
    private static String convertArrayTypeName(String arrayTypeName) {
        // 计算数组维度
        int dimensions = 0;
        int index = 0;
        while (index < arrayTypeName.length() && arrayTypeName.charAt(index) == '[') {
            dimensions++;
            index++;
        }

        // 构建数组后缀
        StringBuilder suffix = new StringBuilder();
        for (int i = 0; i < dimensions; i++) {
            suffix.append("[]");
        }

        // 获取元素类型
        if (index >= arrayTypeName.length()) {
            return "Unknown" + suffix.toString();
        }

        char typeChar = arrayTypeName.charAt(index);
        String elementType;

        switch (typeChar) {
            case 'Z':
                elementType = "boolean";
                break;
            case 'B':
                elementType = "byte";
                break;
            case 'C':
                elementType = "char";
                break;
            case 'D':
                elementType = "double";
                break;
            case 'F':
                elementType = "float";
                break;
            case 'I':
                elementType = "int";
                break;
            case 'J':
                elementType = "long";
                break;
            case 'S':
                elementType = "short";
                break;
            case 'L':
                // 对象类型，格式为 Ljava.lang.String;
                int semicolonIndex = arrayTypeName.indexOf(';', index);
                if (semicolonIndex > index) {
                    elementType = arrayTypeName.substring(index + 1, semicolonIndex);
                } else {
                    elementType = "Unknown";
                }
                break;
            default:
                elementType = "Unknown";
                break;
        }

        return elementType + suffix.toString();
    }

    /**
     * 判断是否应该跳过的调用者类
     */
    private static boolean shouldSkipCaller(String className) {
        return className.startsWith("com.codezj.") ||
               className.startsWith("java.lang.reflect.") ||
               className.startsWith("sun.reflect.") ||
               className.startsWith("java.") ||
               className.startsWith("javax.") ||
               className.startsWith("sun.") ||
               className.startsWith("org.springframework.cglib.") ||
               className.startsWith("org.springframework.aop.") ||
               className.startsWith("net.sf.cglib.") ||
               className.contains("$$EnhancerBySpringCGLIB$$") ||
               className.contains("$$FastClassBySpringCGLIB$$") ||
               className.contains("$Proxy") ||
               className.contains("$$Lambda$");
    }

    /**
     * 记录方法异常
     */
    public static void logMethodException(String className, String methodName, Throwable exception) {
        try {
            // 弹出当前方法
            Deque<MethodCall> stack = callStack.get();
            if (!stack.isEmpty()) {
                MethodCall currentMethod = stack.peek();
                if (currentMethod != null &&
                    currentMethod.getClassName().equals(className) &&
                    currentMethod.getMethodName().equals(methodName)) {
                    stack.pop();
                }
            }

        } catch (Exception e) {
            logger.warn("[WARN] Failed to log method exception: {}", e.getMessage());
        }
    }

    /**
     * 定期清理检查
     */
    private static void cleanupIfNeeded() {
        Deque<MethodCall> stack = callStack.get();
        if (stack.size() > 50) { // 如果栈深度异常
            logger.warn("[WARN] Abnormal stack depth: {}, possible missing method exit calls", stack.size());
        }
    }

}
